/**
 * @brief 状态机模块
 * @version 1.0
 * @author 宋炜
 * @date 2024-5-15
 */

const { OrderedMap , OrderedSet , LinkList } = require( 'js-sdsl' );

class stateNode 
{
        m_name__ = null
        m_on_ent_cb__ = null
        /**
         * 注意： 离开函数需要返回一个对象，对象包含两个数据，一个是下一个节点的名字，另外是标记是否是自动转换的标记
         *    当type是是false的时候自动转换。实际使用的时候，在执行一次触发操作，下一次的转换如果是自动转换则返回false
         *    { name: <下一个状态的名字>, type: false }
         */
        m_on_leave_cb__ = null

        constructor( name )
        {
                this.m_name__ = name;
        }

        on( type , cb ){
                if( typeof( cb ) != 'function' ){
                        throw '回调函数必须是函数类型'
                }

                if( type == 'enter'){
                        this.m_on_ent_cb__ = cb;
                }else if( type == 'leave' ){
                        this.m_on_leave_cb__ = cb;
                }
        }
        /**
         * 调用事件响应函数
         * @param {*} type 
         * @param  {...any} param 
         */
        call( type , ...param )
        {
                if( type ){
                        this.m_on_ent_cb__( ...param );
                }else{
                        this.m_on_leave_cb__( ...param );
                }
        }
};

class transArc
{
        m_src__ = null
        m_dst__ = null

        m_raise_cb__ = null;        // 当执行转换的时候调用

        constructor( src , dst )
        {
                this.m_src__ = src;
                this.m_dst__ = dst;
        }

        on( type , cb )
        {
                if( typeof( cb ) != 'function' ){
                        throw '回调函数必须是函数类型'
                }
                if( type == 'raise' ){
                        this.m_raise_cb__ = cb;
                }
        }

        call( ...param )
        {
                if( this.m_raise_cb__ ){
                        this.m_raise_cb__( ...param )
                }
        }
}

class stateInnerData 
{
        m_data__ = new LinkList();

        constructor(){}

        push( data ){
                this.m_data__.pushBack( data );
        }

        clear(){
                this.m_data__.clear();
        }

        getLast()
        {
                return this.m_data__.back();
        }

        get(){
                let ret = new Array()
                this.m_data__.forEach( ( e )=>{
                        ret.push( e );
                })

                return ret;
        }

        count(){
                this.m_data__.size();
        }
}

class state 
{
        m_start_state__ = null                  // 入口状态
        m_end_state__   = null                  // 结束状态

        m_current_statue__ = null;

        m_states__ = new OrderedMap()
        m_arcs__ = new OrderedMap()
        m_is_running__ = false                  // 运行状态

        m_inner_data__ = new stateInnerData();

        CONST_STATE_CALL = { ENT: true , LEAVE: false };

        constructor(){}

        /**
         * 添加节点，转换，同时指定响应函数
         * @param {*} from 出发状态
         * @param {*} to 目的状态
         * @param {*} ent 目的状态进入触发函数
         * @param {*} leave 出发状态离开触发函数
         * @param {*} raise 转换的时候，离开之后，到达之前调用的函数，可以为null
         */
        add( from , to , ent , leave , raise )
        {
                let from_obj = this.m_states__.getElementByKey( from ),
                    to_obj = this.m_states__.getElementByKey( to );
                if( !from_obj ){
                        from_obj = new stateNode( from );
                        this.m_states__.setElement( from , from_obj );
                }

                from_obj.on( "leave" , leave );

                if( !to_obj ){
                        to_obj = new stateNode( to );
                        this.m_states__.setElement( to , to_obj )
                }
                to_obj.on( 'enter' , ent );
                let arc = this.m_arcs__.getElementByKey( from );
                
                if( !arc ){
                        arc = new OrderedSet;                       
                        this.m_arcs__.setElement( from , arc );
                }

                let dst = arc.getElementByKey( to );
                if( !dst ){
                        let arc_1 = new transArc( from , to );
                        arc.setElement( to , arc_1 );
                        arc_1.on( 'raise' , raise)
                }else{
                        throw "转换已经存在"
                }
        }
        /**
         * 
         * @param {*} from 
         * @param {*} to 
         */
        addArc( from , to )
        {
                let from_obj = this.m_states__.getElementByKey( from ),
                    to_obj = this.m_states__.getElementByKey( to );
                if( !from_obj ){
                        from_obj = new stateNode( from );
                        this.m_states__.setElement( from , from_obj );
                }

                if( !to_obj ){
                        to_obj = new stateNode( to );
                        this.m_states__.setElement( to , to_obj )
                }

                let arc = this.m_arcs__.getElementByKey( from );
                
                if( !arc ){
                        arc = new OrderedSet;                       
                        this.m_arcs__.setElement( from , arc );
                }

                let dst = arc.getElementByKey( to );
                if( !dst ){
                        let arc_1 = new transArc( from , to );
                        arc.setElement( to , arc_1 );
                }else{
                        throw "转换已经存在"
                }
        }
        /**
         * 
         * @param {*} name 
         * @param {*} ent 
         * @param {*} leave 
         */
        addState( name , ent , leave )
        {
                let from_obj = this.m_states__.getElementByKey( name );
                if( !from_obj ){
                        from_obj = new stateNode( from );
                        this.m_states__.setElement( from , from_obj );
                }

                from_obj.on( 'enter' , ent );
                from_obj.on( 'leave' , leave );
        }
        /**
         * 
         * @param {*} name 
         * @param {*} ent 
         * @param {*} leave 
         */
        setState( name , ent , leave )
        {
                let from_obj = this.m_states__.getElementByKey( name );
                if( from_obj ){
                        from_obj.on( 'enter' , ent );
                        from_obj.on( 'leave' , leave );
                }
        }
        /**
         * 
         * @param {*} name 
         * @param {*} cb 
         */
        setEnterCall( name , cb )
        {
                let from_obj = this.m_states__.getElementByKey( name );
                if( from_obj ){
                        from_obj.on( 'enter' , cb );
                }
        }
        /**
         * 
         * @param {*} name 
         * @param {*} cb 
         */
        setLeaveCall( name , cb )
        {
                let from_obj = this.m_states__.getElementByKey( name );
                if( from_obj ){
                        from_obj.on( 'leave' , cb );
                }
        }
        /**
         * 
         * @param {*} name 
         */
        setStart( name )
        {
                this.m_start_state__ = name;
        }
        /**
         * 
         * @param {*} from 
         * @param {*} to 
         * @param {*} cb 
         */
        setRaise( from , to , cb )
        {
                let arc = this.et_arc__( from , to );
                if( arc ){
                        arc.on( 'raise' , cb );
                }
        }
        /**
         * 
         * @param {*} name 
         */
        setEnd( name )
        {
                this.m_end_state__ = name;
        }
        /**
         * 
         * @param {*} from 
         * @param {*} to 
         */
        get_arc__( from , to )
        {
                let sets = this.m_arcs__.getElementByKey( from )
                if( sets ){
                        return sets.getElementByKey( to );
                }

                return null;
        }
        /**
         * 启动状态机
         * @param {I} sw 启动或者停止状态机
         */
        start( sw )
        {
                this.m_is_running__ = sw;

                if( sw == true ){
                        this.m_current_statue__ = this.m_states__.getElementByKey( this.m_start_state__ );
                        this.m_current_statue__.call( this.CONST_STATE_CALL.ENT , this.m_current_statue__ )
                }else{
                        this.m_current_statue__ = null;
                }
        }
        /**
         * @brief 执行自动转换操作，在触发操作中调用
         */
        async next__()
        {
                let arc = this.get_arc__( this.m_current_statue__.getName() , next_name.name );
                if( !arc ){
                        throw '路径不存在';
                }
                // 执行离开操作，并且获取下一个转换节点
                let next_name = await this.m_current_statue__.call( this.CONST_STATE_CALL.LEAVE , this.m_current_statue__ , this.m_inner_data__ );
                // 执行转换弧触发操作
                await arc.call( this.m_current_statue__ , this.m_inner_data__ );

                // 执行转换操作
                this.m_current_statue__ = this.m_states__.getElementByKey( next_name.name );

                await this.m_current_statue__.call( this.m_current_statue__ , this.m_inner_data__ );

                if( next_name.name == this.m_end_state__ ){
                        this.m_inner_data__.clear();
                        console.log( '状态机已经运行到结束节点' );
                }

                if( next_name.type == false ){
                        await this.next__();
                }
        }

        /**
         * 执行一次转换，从外部调用。当转换后遇到自动转换的情况，则自动进行一次转换。
         * @param {*} data 触发数据，通常情况下将触发的数据填入一次运行周期的中。当本周期结束后清理这些数据
         */
        async next( data )
        {
                this.m_inner_data__.push( data );
                // 完成
                let arc = this.get_arc__( this.m_current_statue__.getName() , next_name.name );
                if( !arc ){
                        throw '路径不存在';
                }
                // 执行离开操作，并且获取下一个转换节点。
                let next_name = await this.m_current_statue__.call( this.CONST_STATE_CALL.LEAVE , this.m_current_statue__ , this.m_inner_data__ );
                // 执行转换弧触发操作
                await arc.call( this.m_current_statue__ , this.m_inner_data__ );

                // 执行转换操作
                this.m_current_statue__ = this.m_states__.getElementByKey( next_name.name );
                if( next_name.name == this.m_end_state__ ){
                        this.m_inner_data__.clear();
                        console.log( '状态机已经运行到结束节点' );
                }

                await this.m_current_statue__.call( this.m_current_statue__ , this.m_inner_data__ );
                if( next_name.type == false ){
                        await this.next__();
                }
        }
}

module.exports = state;